home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkScale.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-21  |  43.3 KB  |  1,421 lines

  1. /* 
  2.  * tkScale.c --
  3.  *
  4.  *    This module implements a scale widgets for the Tk toolkit.
  5.  *    A scale displays a slider that can be adjusted to change a
  6.  *    value;  it also displays numeric labels and a textual label,
  7.  *    if desired.
  8.  *
  9.  * Copyright 1990 Regents of the University of California.
  10.  * Permission to use, copy, modify, and distribute this
  11.  * software and its documentation for any purpose and without
  12.  * fee is hereby granted, provided that the above copyright
  13.  * notice appear in all copies.  The University of California
  14.  * makes no representations about the suitability of this
  15.  * software for any purpose.  It is provided "as is" without
  16.  * express or implied warranty.
  17.  */
  18.  
  19. #ifndef lint
  20. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkScale.c,v 1.28 92/08/21 11:45:25 ouster Exp $ SPRITE (Berkeley)";
  21. #endif
  22.  
  23. #include "tkConfig.h"
  24. #include "default.h"
  25. #include "tkInt.h"
  26.  
  27. /*
  28.  * A data structure of the following type is kept for each scale
  29.  * widget managed by this file:
  30.  */
  31.  
  32. typedef struct {
  33.     Tk_Window tkwin;        /* Window that embodies the scale.  NULL
  34.                  * means that the window has been destroyed
  35.                  * but the data structures haven't yet been
  36.                  * cleaned up.*/
  37.     Tcl_Interp *interp;        /* Interpreter associated with scale. */
  38.     Tk_Uid orientUid;        /* Orientation for window ("vertical" or
  39.                  * "horizontal"). */
  40.     int vertical;        /* Non-zero means vertical orientation,
  41.                  * zero means horizontal. */
  42.     int value;            /* Current value of scale. */
  43.     int fromValue;        /* Value corresponding to left or top of
  44.                  * scale. */
  45.     int toValue;        /* Value corresponding to right or bottom
  46.                  * of scale. */
  47.     int tickInterval;        /* Distance between tick marks;  0 means
  48.                  * don't display any tick marks. */
  49.     char *command;        /* Command prefix to use when invoking Tcl
  50.                  * commands because the scale value changed.
  51.                  * NULL means don't invoke commands.
  52.                  * Malloc'ed. */
  53.     int commandLength;        /* Number of non-NULL bytes in command. */
  54.     char *label;        /* Label to display above or to right of
  55.                  * scale;  NULL means don't display a
  56.                  * label.  Malloc'ed. */
  57.     int labelLength;        /* Number of non-NULL chars. in label. */
  58.     Tk_Uid state;        /* Normal or disabled.  Value cannot be
  59.                  * changed when scale is disabled. */
  60.  
  61.     /*
  62.      * Information used when displaying widget:
  63.      */
  64.  
  65.     int borderWidth;        /* Width of 3-D border around window. */
  66.     Tk_3DBorder bgBorder;    /* Used for drawing background. */
  67.     Tk_3DBorder sliderBorder;    /* Used for drawing slider in normal mode. */
  68.     Tk_3DBorder activeBorder;    /* Used for drawing slider when active (i.e.
  69.                  * when mouse is in window). */
  70.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  71.     XColor *textColorPtr;    /* Color for drawing text. */
  72.     GC textGC;            /* GC for drawing text in normal mode. */
  73.     int width;            /* Desired narrow dimension of scale,
  74.                  * in pixels. */
  75.     int length;            /* Desired long dimension of scale,
  76.                  * in pixels. */
  77.     int relief;            /* Indicates whether window as a whole is
  78.                  * raised, sunken, or flat. */
  79.     int offset;            /* Zero if relief is TK_RELIEF_FLAT,
  80.                  * borderWidth otherwise.   Indicates how
  81.                  * much interior stuff must be offset from
  82.                  * outside edges to leave room for border. */
  83.     int sliderLength;        /* Length of slider, measured in pixels along
  84.                  * long dimension of scale. */
  85.     int showValue;        /* Non-zero means to display the scale value
  86.                  * below or to the left of the slider;  zero
  87.                  * means don't display the value. */
  88.     int tickPixels;        /* Number of pixels required for widest tick
  89.                  * mark.  0 means don't display ticks.*/
  90.     int valuePixels;        /* Number of pixels required for value text. */
  91.     int labelPixels;        /* Number of pixels required for label.   0
  92.                  * means don't display label. */
  93.  
  94.     /*
  95.      * Miscellaneous information:
  96.      */
  97.  
  98.     Cursor cursor;        /* Current cursor for window, or None. */
  99.     int flags;            /* Various flags;  see below for
  100.                  * definitions. */
  101. } Scale;
  102.  
  103. /*
  104.  * Flag bits for scales:
  105.  *
  106.  * REDRAW_SLIDER -        1 means slider (and numerical readout) need
  107.  *                to be redrawn.
  108.  * REDRAW_OTHER -        1 means other stuff besides slider and value
  109.  *                need to be redrawn.
  110.  * REDRAW_ALL -            1 means the entire widget needs to be redrawn.
  111.  * ACTIVE -            1 means the widget is active (the mouse is
  112.  *                in its window).
  113.  * BUTTON_PRESSED -        1 means a button press is in progress, so
  114.  *                slider should appear depressed and should be
  115.  *                draggable.
  116.  */
  117.  
  118. #define REDRAW_SLIDER        1
  119. #define REDRAW_OTHER        2
  120. #define REDRAW_ALL        3
  121. #define ACTIVE            4
  122. #define BUTTON_PRESSED        8
  123.  
  124. /*
  125.  * Space to leave between scale area and text.
  126.  */
  127.  
  128. #define SPACING 2
  129.  
  130. /*
  131.  * Information used for argv parsing.
  132.  */
  133.  
  134.  
  135. static Tk_ConfigSpec configSpecs[] = {
  136.     {TK_CONFIG_BORDER, "-activeforeground", "activeForeground", "Background",
  137.     DEF_SCALE_ACTIVE_FG_COLOR, Tk_Offset(Scale, activeBorder),
  138.     TK_CONFIG_COLOR_ONLY},
  139.     {TK_CONFIG_BORDER, "-activeforeground", "activeForeground", "Background",
  140.     DEF_SCALE_ACTIVE_FG_MONO, Tk_Offset(Scale, activeBorder),
  141.     TK_CONFIG_MONO_ONLY},
  142.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  143.     DEF_SCALE_BG_COLOR, Tk_Offset(Scale, bgBorder),
  144.     TK_CONFIG_COLOR_ONLY},
  145.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  146.     DEF_SCALE_BG_MONO, Tk_Offset(Scale, bgBorder),
  147.     TK_CONFIG_MONO_ONLY},
  148.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  149.     (char *) NULL, 0, 0},
  150.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  151.     (char *) NULL, 0, 0},
  152.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  153.     DEF_SCALE_BORDER_WIDTH, Tk_Offset(Scale, borderWidth), 0},
  154.     {TK_CONFIG_STRING, "-command", "command", "Command",
  155.     (char *) NULL, Tk_Offset(Scale, command), 0},
  156.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  157.     DEF_SCALE_CURSOR, Tk_Offset(Scale, cursor), TK_CONFIG_NULL_OK},
  158.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  159.     (char *) NULL, 0, 0},
  160.     {TK_CONFIG_FONT, "-font", "font", "Font",
  161.     DEF_SCALE_FONT, Tk_Offset(Scale, fontPtr),
  162.     0},
  163.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  164.     DEF_SCALE_FG_COLOR, Tk_Offset(Scale, textColorPtr),
  165.     TK_CONFIG_COLOR_ONLY},
  166.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  167.     DEF_SCALE_FG_MONO, Tk_Offset(Scale, textColorPtr),
  168.     TK_CONFIG_MONO_ONLY},
  169.     {TK_CONFIG_INT, "-from", "from", "From",
  170.     DEF_SCALE_FROM, Tk_Offset(Scale, fromValue), 0},
  171.     {TK_CONFIG_STRING, "-label", "label", "Label",
  172.     DEF_SCALE_LABEL, Tk_Offset(Scale, label), 0},
  173.     {TK_CONFIG_PIXELS, "-length", "length", "Length",
  174.     DEF_SCALE_LENGTH, Tk_Offset(Scale, length), 0},
  175.     {TK_CONFIG_UID, "-orient", "orient", "Orient",
  176.     DEF_SCALE_ORIENT, Tk_Offset(Scale, orientUid), 0},
  177.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  178.     DEF_SCALE_RELIEF, Tk_Offset(Scale, relief), 0},
  179.     {TK_CONFIG_BOOLEAN, "-showvalue", "showValue", "ShowValue",
  180.     DEF_SCALE_SHOW_VALUE, Tk_Offset(Scale, showValue), 0},
  181.     {TK_CONFIG_BORDER, "-sliderforeground", "sliderForeground", "Background",
  182.     DEF_SCALE_SLIDER_FG_COLOR, Tk_Offset(Scale, sliderBorder),
  183.     TK_CONFIG_COLOR_ONLY},
  184.     {TK_CONFIG_BORDER, "-sliderforeground", "sliderForeground", "Background",
  185.     DEF_SCALE_SLIDER_FG_MONO, Tk_Offset(Scale, sliderBorder), 
  186.     TK_CONFIG_MONO_ONLY},
  187.     {TK_CONFIG_PIXELS, "-sliderlength", "sliderLength", "SliderLength",
  188.     DEF_SCALE_SLIDER_LENGTH, Tk_Offset(Scale, sliderLength), 0},
  189.     {TK_CONFIG_UID, "-state", "state", "State",
  190.     DEF_SCALE_STATE, Tk_Offset(Scale, state), 0},
  191.     {TK_CONFIG_INT, "-tickinterval", "tickInterval", "TickInterval",
  192.     DEF_SCALE_TICK_INTERVAL, Tk_Offset(Scale, tickInterval), 0},
  193.     {TK_CONFIG_INT, "-to", "to", "To",
  194.     DEF_SCALE_TO, Tk_Offset(Scale, toValue), 0},
  195.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  196.     DEF_SCALE_WIDTH, Tk_Offset(Scale, width), 0},
  197.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  198.     (char *) NULL, 0, 0}
  199. };
  200.  
  201. /*
  202.  * Forward declarations for procedures defined later in this file:
  203.  */
  204.  
  205. static void        ComputeScaleGeometry _ANSI_ARGS_((Scale *scalePtr));
  206. static int        ConfigureScale _ANSI_ARGS_((Tcl_Interp *interp,
  207.                 Scale *scalePtr, int argc, char **argv,
  208.                 int flags));
  209. static void        DestroyScale _ANSI_ARGS_((ClientData clientData));
  210. static void        DisplayHorizontalScale _ANSI_ARGS_((
  211.                 ClientData clientData));
  212. static void        DisplayHorizontalValue _ANSI_ARGS_((Scale *scalePtr,
  213.                 int value, int bottom));
  214. static void        DisplayVerticalScale _ANSI_ARGS_((
  215.                 ClientData clientData));
  216. static void        DisplayVerticalValue _ANSI_ARGS_((Scale *scalePtr,
  217.                 int value, int rightEdge));
  218. static void        EventuallyRedrawScale _ANSI_ARGS_((Scale *scalePtr,
  219.                 int what));
  220. static int        PixelToValue _ANSI_ARGS_((Scale *scalePtr, int x,
  221.                 int y));
  222. static void        ScaleEventProc _ANSI_ARGS_((ClientData clientData,
  223.                 XEvent *eventPtr));
  224. static void        ScaleMouseProc _ANSI_ARGS_((ClientData clientData,
  225.                 XEvent *eventPtr));
  226. static int        ScaleWidgetCmd _ANSI_ARGS_((ClientData clientData,
  227.                 Tcl_Interp *interp, int argc, char **argv));
  228. static void        SetScaleValue _ANSI_ARGS_((Scale *scalePtr,
  229.                 int value));
  230. static int        ValueToPixel _ANSI_ARGS_((Scale *scalePtr, int value));
  231.  
  232. /*
  233.  *--------------------------------------------------------------
  234.  *
  235.  * Tk_ScaleCmd --
  236.  *
  237.  *    This procedure is invoked to process the "scale" Tcl
  238.  *    command.  See the user documentation for details on what
  239.  *    it does.
  240.  *
  241.  * Results:
  242.  *    A standard Tcl result.
  243.  *
  244.  * Side effects:
  245.  *    See the user documentation.
  246.  *
  247.  *--------------------------------------------------------------
  248.  */
  249.  
  250. int
  251. Tk_ScaleCmd(clientData, interp, argc, argv)
  252.     ClientData clientData;        /* Main window associated with
  253.                  * interpreter. */
  254.     Tcl_Interp *interp;        /* Current interpreter. */
  255.     int argc;            /* Number of arguments. */
  256.     char **argv;        /* Argument strings. */
  257. {
  258.     Tk_Window tkwin = (Tk_Window) clientData;
  259.     register Scale *scalePtr;
  260.     Tk_Window new;
  261.  
  262.     if (argc < 2) {
  263.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  264.         argv[0], " pathName ?options?\"", (char *) NULL);
  265.     return TCL_ERROR;
  266.     }
  267.  
  268.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  269.     if (new == NULL) {
  270.     return TCL_ERROR;
  271.     }
  272.  
  273.     /*
  274.      * Initialize fields that won't be initialized by ConfigureScale,
  275.      * or which ConfigureScale expects to have reasonable values
  276.      * (e.g. resource pointers).
  277.      */
  278.  
  279.     scalePtr = (Scale *) ckalloc(sizeof(Scale));
  280.     scalePtr->tkwin = new;
  281.     scalePtr->interp = interp;
  282.     scalePtr->value = 0;
  283.     scalePtr->command = NULL;
  284.     scalePtr->label = NULL;
  285.     scalePtr->state = tkNormalUid;
  286.     scalePtr->bgBorder = NULL;
  287.     scalePtr->sliderBorder = NULL;
  288.     scalePtr->activeBorder = NULL;
  289.     scalePtr->fontPtr = NULL;
  290.     scalePtr->textColorPtr = NULL;
  291.     scalePtr->textGC = None;
  292.     scalePtr->cursor = None;
  293.     scalePtr->flags = 0;
  294.  
  295.     Tk_SetClass(scalePtr->tkwin, "Scale");
  296.     Tk_CreateEventHandler(scalePtr->tkwin, ExposureMask|StructureNotifyMask,
  297.         ScaleEventProc, (ClientData) scalePtr);
  298.     Tk_CreateEventHandler(scalePtr->tkwin, EnterWindowMask|LeaveWindowMask
  299.         |PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
  300.         ScaleMouseProc, (ClientData) scalePtr);
  301.     Tcl_CreateCommand(interp, Tk_PathName(scalePtr->tkwin), ScaleWidgetCmd,
  302.         (ClientData) scalePtr, (void (*)()) NULL);
  303.     if (ConfigureScale(interp, scalePtr, argc-2, argv+2, 0) != TCL_OK) {
  304.     goto error;
  305.     }
  306.  
  307.     interp->result = Tk_PathName(scalePtr->tkwin);
  308.     return TCL_OK;
  309.  
  310.     error:
  311.     Tk_DestroyWindow(scalePtr->tkwin);
  312.     return TCL_ERROR;
  313. }
  314.  
  315. /*
  316.  *--------------------------------------------------------------
  317.  *
  318.  * ScaleWidgetCmd --
  319.  *
  320.  *    This procedure is invoked to process the Tcl command
  321.  *    that corresponds to a widget managed by this module.
  322.  *    See the user documentation for details on what it does.
  323.  *
  324.  * Results:
  325.  *    A standard Tcl result.
  326.  *
  327.  * Side effects:
  328.  *    See the user documentation.
  329.  *
  330.  *--------------------------------------------------------------
  331.  */
  332.  
  333. static int
  334. ScaleWidgetCmd(clientData, interp, argc, argv)
  335.     ClientData clientData;        /* Information about scale
  336.                      * widget. */
  337.     Tcl_Interp *interp;            /* Current interpreter. */
  338.     int argc;                /* Number of arguments. */
  339.     char **argv;            /* Argument strings. */
  340. {
  341.     register Scale *scalePtr = (Scale *) clientData;
  342.     int result = TCL_OK;
  343.     int length;
  344.     char c;
  345.  
  346.     if (argc < 2) {
  347.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  348.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  349.     return TCL_ERROR;
  350.     }
  351.     Tk_Preserve((ClientData) scalePtr);
  352.     c = argv[1][0];
  353.     length = strlen(argv[1]);
  354.     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  355.     if (argc == 2) {
  356.         result = Tk_ConfigureInfo(interp, scalePtr->tkwin, configSpecs,
  357.             (char *) scalePtr, (char *) NULL, 0);
  358.     } else if (argc == 3) {
  359.         result = Tk_ConfigureInfo(interp, scalePtr->tkwin, configSpecs,
  360.             (char *) scalePtr, argv[2], 0);
  361.     } else {
  362.         result = ConfigureScale(interp, scalePtr, argc-2, argv+2,
  363.             TK_CONFIG_ARGV_ONLY);
  364.     }
  365.     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  366.     if (argc != 2) {
  367.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  368.             argv[0], " get\"", (char *) NULL);
  369.         goto error;
  370.     }
  371.     sprintf(interp->result, "%d", scalePtr->value);
  372.     } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
  373.     int value;
  374.  
  375.     if (argc != 3) {
  376.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  377.             argv[0], " set value\"", (char *) NULL);
  378.         goto error;
  379.     }
  380.     if (Tcl_GetInt(interp, argv[2], &value) != TCL_OK) {
  381.         goto error;
  382.     }
  383.     if (scalePtr->state == tkNormalUid) {
  384.         if ((value < scalePtr->fromValue)
  385.             ^ (scalePtr->toValue < scalePtr->fromValue)) {
  386.         value = scalePtr->fromValue;
  387.         }
  388.         if ((value > scalePtr->toValue)
  389.             ^ (scalePtr->toValue < scalePtr->fromValue)) {
  390.         value = scalePtr->toValue;
  391.         }
  392.         SetScaleValue(scalePtr, value);
  393.     }
  394.     } else {
  395.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  396.         "\":  must be configure, get, or set", (char *) NULL);
  397.     goto error;
  398.     }
  399.     Tk_Release((ClientData) scalePtr);
  400.     return result;
  401.  
  402.     error:
  403.     Tk_Release((ClientData) scalePtr);
  404.     return TCL_ERROR;
  405. }
  406.  
  407. /*
  408.  *----------------------------------------------------------------------
  409.  *
  410.  * DestroyScale --
  411.  *
  412.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  413.  *    to clean up the internal structure of a button at a safe time
  414.  *    (when no-one is using it anymore).
  415.  *
  416.  * Results:
  417.  *    None.
  418.  *
  419.  * Side effects:
  420.  *    Everything associated with the scale is freed up.
  421.  *
  422.  *----------------------------------------------------------------------
  423.  */
  424.  
  425. static void
  426. DestroyScale(clientData)
  427.     ClientData clientData;    /* Info about scale widget. */
  428. {
  429.     register Scale *scalePtr = (Scale *) clientData;
  430.  
  431.     if (scalePtr->command != NULL) {
  432.     ckfree(scalePtr->command);
  433.     }
  434.     if (scalePtr->label != NULL) {
  435.     ckfree(scalePtr->label);
  436.     }
  437.     if (scalePtr->bgBorder != NULL) {
  438.     Tk_Free3DBorder(scalePtr->bgBorder);
  439.     }
  440.     if (scalePtr->sliderBorder != NULL) {
  441.     Tk_Free3DBorder(scalePtr->sliderBorder);
  442.     }
  443.     if (scalePtr->activeBorder != NULL) {
  444.     Tk_Free3DBorder(scalePtr->activeBorder);
  445.     }
  446.     if (scalePtr->fontPtr != NULL) {
  447.     Tk_FreeFontStruct(scalePtr->fontPtr);
  448.     }
  449.     if (scalePtr->textColorPtr != NULL) {
  450.     Tk_FreeColor(scalePtr->textColorPtr);
  451.     }
  452.     if (scalePtr->textGC != None) {
  453.     Tk_FreeGC(scalePtr->textGC);
  454.     }
  455.     if (scalePtr->cursor != None) {
  456.     Tk_FreeCursor(scalePtr->cursor);
  457.     }
  458.     ckfree((char *) scalePtr);
  459. }
  460.  
  461. /*
  462.  *----------------------------------------------------------------------
  463.  *
  464.  * ConfigureScale --
  465.  *
  466.  *    This procedure is called to process an argv/argc list, plus
  467.  *    the Tk option database, in order to configure (or
  468.  *    reconfigure) a scale widget.
  469.  *
  470.  * Results:
  471.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  472.  *    returned, then interp->result contains an error message.
  473.  *
  474.  * Side effects:
  475.  *    Configuration information, such as colors, border width,
  476.  *    etc. get set for scalePtr;  old resources get freed,
  477.  *    if there were any.
  478.  *
  479.  *----------------------------------------------------------------------
  480.  */
  481.  
  482. static int
  483. ConfigureScale(interp, scalePtr, argc, argv, flags)
  484.     Tcl_Interp *interp;        /* Used for error reporting. */
  485.     register Scale *scalePtr;    /* Information about widget;  may or may
  486.                  * not already have values for some fields. */
  487.     int argc;            /* Number of valid entries in argv. */
  488.     char **argv;        /* Arguments. */
  489.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  490. {
  491.     XGCValues gcValues;
  492.     GC newGC;
  493.     int length;
  494.  
  495.     if (Tk_ConfigureWidget(interp, scalePtr->tkwin, configSpecs,
  496.         argc, argv, (char *) scalePtr, flags) != TCL_OK) {
  497.     return TCL_ERROR;
  498.     }
  499.  
  500.     /*
  501.      * A few options need special processing, such as parsing the
  502.      * orientation or setting the background from a 3-D border.
  503.      */
  504.  
  505.     length = strlen(scalePtr->orientUid);
  506.     if (strncmp(scalePtr->orientUid, "vertical", length) == 0) {
  507.     scalePtr->vertical = 1;
  508.     } else if (strncmp(scalePtr->orientUid, "horizontal", length) == 0) {
  509.     scalePtr->vertical = 0;
  510.     } else {
  511.     Tcl_AppendResult(interp, "bad orientation \"", scalePtr->orientUid,
  512.         "\": must be vertical or horizontal", (char *) NULL);
  513.     return TCL_ERROR;
  514.     }
  515.  
  516.     if ((scalePtr->state != tkNormalUid)
  517.         && (scalePtr->state != tkDisabledUid)) {
  518.     Tcl_AppendResult(interp, "bad state value \"", scalePtr->state,
  519.         "\":  must be normal or disabled", (char *) NULL);
  520.     scalePtr->state = tkNormalUid;
  521.     return TCL_ERROR;
  522.     }
  523.  
  524.     /*
  525.      * Make sure that the tick interval has the right sign so that
  526.      * addition moves from fromValue to toValue.
  527.      */
  528.  
  529.     if ((scalePtr->tickInterval < 0)
  530.         ^ ((scalePtr->toValue - scalePtr->fromValue) <  0)) {
  531.     scalePtr->tickInterval = -scalePtr->tickInterval;
  532.     }
  533.  
  534.     /*
  535.      * Set the scale value to itself;  all this does is to make sure
  536.      * that the scale's value is within the new acceptable range for
  537.      * the scale.
  538.      */
  539.  
  540.     SetScaleValue(scalePtr, scalePtr->value);
  541.  
  542.     if (scalePtr->command != NULL) {
  543.     scalePtr->commandLength = strlen(scalePtr->command);
  544.     } else {
  545.     scalePtr->commandLength = 0;
  546.     }
  547.  
  548.     if (scalePtr->label != NULL) {
  549.     scalePtr->labelLength = strlen(scalePtr->label);
  550.     } else {
  551.     scalePtr->labelLength = 0;
  552.     }
  553.  
  554.     Tk_SetBackgroundFromBorder(scalePtr->tkwin, scalePtr->bgBorder);
  555.  
  556.     gcValues.font = scalePtr->fontPtr->fid;
  557.     gcValues.foreground = scalePtr->textColorPtr->pixel;
  558.     newGC = Tk_GetGC(scalePtr->tkwin, GCForeground|GCFont, &gcValues);
  559.     if (scalePtr->textGC != None) {
  560.     Tk_FreeGC(scalePtr->textGC);
  561.     }
  562.     scalePtr->textGC = newGC;
  563.  
  564.     if (scalePtr->relief != TK_RELIEF_FLAT) {
  565.     scalePtr->offset = scalePtr->borderWidth;
  566.     } else {
  567.     scalePtr->offset = 0;
  568.     }
  569.  
  570.     /*
  571.      * Recompute display-related information, and let the geometry
  572.      * manager know how much space is needed now.
  573.      */
  574.  
  575.     ComputeScaleGeometry(scalePtr);
  576.  
  577.     EventuallyRedrawScale(scalePtr, REDRAW_ALL);
  578.     return TCL_OK;
  579. }
  580.  
  581. /*
  582.  *----------------------------------------------------------------------
  583.  *
  584.  * ComputeScaleGeometry --
  585.  *
  586.  *    This procedure is called to compute various geometrical
  587.  *    information for a scale, such as where various things get
  588.  *    displayed.  It's called when the window is reconfigured.
  589.  *
  590.  * Results:
  591.  *    None.
  592.  *
  593.  * Side effects:
  594.  *    Display-related numbers get changed in *scrollPtr.  The
  595.  *    geometry manager gets told about the window's preferred size.
  596.  *
  597.  *----------------------------------------------------------------------
  598.  */
  599.  
  600. static void
  601. ComputeScaleGeometry(scalePtr)
  602.     register Scale *scalePtr;        /* Information about widget. */
  603. {
  604.     XCharStruct bbox;
  605.     char valueString[30];
  606.     int dummy, lineHeight;
  607.  
  608.     /*
  609.      * Horizontal scales are simpler than vertical ones because
  610.      * all sizes are the same (the height of a line of text);
  611.      * handle them first and then quit.
  612.      */
  613.  
  614.     if (!scalePtr->vertical) {
  615.     lineHeight = scalePtr->fontPtr->ascent + scalePtr->fontPtr->descent;
  616.     if (scalePtr->tickInterval != 0) {
  617.         scalePtr->tickPixels = lineHeight;
  618.     } else {
  619.         scalePtr->tickPixels = 0;
  620.     }
  621.     if (scalePtr->showValue) {
  622.         scalePtr->valuePixels = lineHeight + SPACING;
  623.     } else {
  624.         scalePtr->valuePixels = 0;
  625.     }
  626.     if (scalePtr->labelLength != 0) {
  627.         scalePtr->labelPixels = lineHeight;
  628.     } else {
  629.         scalePtr->labelPixels = 0;
  630.     }
  631.  
  632.     Tk_GeometryRequest(scalePtr->tkwin,
  633.         scalePtr->length + 2*scalePtr->offset,
  634.         scalePtr->tickPixels + scalePtr->valuePixels
  635.         + scalePtr->width + 2*scalePtr->borderWidth
  636.         + scalePtr->labelPixels + 2*scalePtr->offset);
  637.     Tk_SetInternalBorder(scalePtr->tkwin, scalePtr->borderWidth);
  638.     return;
  639.     }
  640.  
  641.     /*
  642.      * Vertical scale:  compute the amount of space needed for tick marks
  643.      * and current value by formatting strings for the two end points;
  644.      * use whichever length is longer.
  645.      */
  646.  
  647.     sprintf(valueString, "%d", scalePtr->fromValue);
  648.     XTextExtents(scalePtr->fontPtr, valueString, strlen(valueString),
  649.         &dummy, &dummy, &dummy, &bbox);
  650.     scalePtr->tickPixels = bbox.rbearing + bbox.lbearing;
  651.     sprintf(valueString, "%d", scalePtr->toValue);
  652.     XTextExtents(scalePtr->fontPtr, valueString, strlen(valueString),
  653.         &dummy, &dummy, &dummy, &bbox);
  654.     if (scalePtr->tickPixels < bbox.rbearing + bbox.lbearing) {
  655.     scalePtr->tickPixels = bbox.rbearing + bbox.lbearing;
  656.     }
  657.  
  658.     /*
  659.      * Pad the value with a bit of extra space for prettier printing.
  660.      */
  661.  
  662.     scalePtr->tickPixels += scalePtr->fontPtr->ascent/2;
  663.     scalePtr->valuePixels = scalePtr->tickPixels;
  664.     if (scalePtr->tickInterval == 0) {
  665.     scalePtr->tickPixels = 0;
  666.     }
  667.     if (!scalePtr->showValue) {
  668.     scalePtr->valuePixels = 0;
  669.     }
  670.  
  671.     if (scalePtr->labelLength == 0) {
  672.     scalePtr->labelPixels = 0;
  673.     } else {
  674.     XTextExtents(scalePtr->fontPtr, scalePtr->label,
  675.         scalePtr->labelLength, &dummy, &dummy, &dummy, &bbox);
  676.     scalePtr->labelPixels = bbox.rbearing + bbox.lbearing
  677.         + scalePtr->fontPtr->ascent;
  678.     }
  679.     Tk_GeometryRequest(scalePtr->tkwin, 4*scalePtr->borderWidth
  680.         + scalePtr->tickPixels + scalePtr->valuePixels + SPACING
  681.         + scalePtr->width + scalePtr->labelPixels,
  682.         scalePtr->length);
  683.     Tk_SetInternalBorder(scalePtr->tkwin, scalePtr->borderWidth);
  684. }
  685.  
  686. /*
  687.  *--------------------------------------------------------------
  688.  *
  689.  * DisplayVerticalScale --
  690.  *
  691.  *    This procedure redraws the contents of a vertical scale
  692.  *    window.  It is invoked as a do-when-idle handler, so it only
  693.  *    runs when there's nothing else for the application to do.
  694.  *
  695.  * Results:
  696.  *    None.
  697.  *
  698.  * Side effects:
  699.  *    Information appears on the screen.
  700.  *
  701.  *--------------------------------------------------------------
  702.  */
  703.  
  704. static void
  705. DisplayVerticalScale(clientData)
  706.     ClientData clientData;    /* Information about widget. */
  707. {
  708.     register Scale *scalePtr = (Scale *) clientData;
  709.     register Tk_Window tkwin = scalePtr->tkwin;
  710.     int tickRightEdge, valueRightEdge, labelLeftEdge, scaleLeftEdge;
  711.     int totalPixels, x, y, width, height, shadowWidth, tickValue;
  712.     int relief;
  713.     Tk_3DBorder sliderBorder;
  714.  
  715.     if ((scalePtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  716.     goto done;
  717.     }
  718.  
  719.     /*
  720.      * Scanning from left to right across the window, the window
  721.      * will contain four columns:  ticks, value, scale, and label.
  722.      * Compute the x-coordinate for each of the columns.
  723.      */
  724.  
  725.     totalPixels = scalePtr->tickPixels + scalePtr->valuePixels
  726.         + 2*scalePtr->borderWidth + scalePtr->width
  727.         + 2*SPACING + scalePtr->labelPixels;
  728.     tickRightEdge = (Tk_Width(tkwin) - totalPixels)/2 + scalePtr->tickPixels;
  729.     valueRightEdge = tickRightEdge + scalePtr->valuePixels;
  730.     scaleLeftEdge = valueRightEdge + SPACING;
  731.     labelLeftEdge = scaleLeftEdge + 2*scalePtr->borderWidth
  732.         + scalePtr->width + scalePtr->fontPtr->ascent/2;
  733.  
  734.     /*
  735.      * Display the information from left to right across the window.
  736.      */
  737.  
  738.     if (scalePtr->flags & REDRAW_OTHER) {
  739.     XClearWindow(Tk_Display(tkwin), Tk_WindowId(tkwin));
  740.  
  741.     /*
  742.      * Display the tick marks.
  743.      */
  744.  
  745.     if (scalePtr->tickPixels != 0) {
  746.         for (tickValue = scalePtr->fromValue; ;
  747.             tickValue += scalePtr->tickInterval) {
  748.         if (scalePtr->toValue > scalePtr->fromValue) {
  749.             if (tickValue > scalePtr->toValue) {
  750.             break;
  751.             }
  752.         } else {
  753.             if (tickValue < scalePtr->toValue) {
  754.             break;
  755.             }
  756.         }
  757.         DisplayVerticalValue(scalePtr, tickValue, tickRightEdge);
  758.         }
  759.     }
  760.     }
  761.  
  762.     /*
  763.      * Display the value, if it is desired.  If not redisplaying the
  764.      * entire window, clear the area of the value to get rid of the
  765.      * old value displayed there.
  766.      */
  767.  
  768.     if (scalePtr->showValue) {
  769.     if (!(scalePtr->flags & REDRAW_OTHER)) {
  770.         XClearArea(Tk_Display(tkwin), Tk_WindowId(tkwin),
  771.             valueRightEdge-scalePtr->valuePixels, scalePtr->offset,
  772.             scalePtr->valuePixels,
  773.             Tk_Height(tkwin) - 2*scalePtr->offset, False);
  774.     }
  775.     DisplayVerticalValue(scalePtr, scalePtr->value, valueRightEdge);
  776.     }
  777.  
  778.     /*
  779.      * Display the scale and the slider.  If not redisplaying the
  780.      * entire window, must clear the trench area to erase the old
  781.      * slider, but don't need to redraw the border.
  782.      */
  783.  
  784.     if (scalePtr->flags & REDRAW_OTHER) {
  785.     Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
  786.         scalePtr->bgBorder, scaleLeftEdge, scalePtr->offset,
  787.         scalePtr->width + 2*scalePtr->borderWidth,
  788.         Tk_Height(tkwin) - 2*scalePtr->offset, scalePtr->borderWidth,
  789.         TK_RELIEF_SUNKEN);
  790.     } else {
  791.     XClearArea(Tk_Display(tkwin), Tk_WindowId(tkwin),
  792.         scaleLeftEdge + scalePtr->borderWidth,
  793.         scalePtr->offset + scalePtr->borderWidth,
  794.         scalePtr->width,
  795.         Tk_Height(tkwin) - 2*scalePtr->offset
  796.         - 2*scalePtr->borderWidth, False);
  797.     }
  798.     if (scalePtr->flags & ACTIVE) {
  799.     sliderBorder = scalePtr->activeBorder;
  800.     } else {
  801.     sliderBorder = scalePtr->sliderBorder;
  802.     }
  803.     width = scalePtr->width;
  804.     height = scalePtr->sliderLength/2;
  805.     x = scaleLeftEdge + scalePtr->borderWidth;
  806.     y = ValueToPixel(scalePtr, scalePtr->value) - height;
  807.     shadowWidth = scalePtr->borderWidth/2;
  808.     if (shadowWidth == 0) {
  809.     shadowWidth = 1;
  810.     }
  811.     relief = (scalePtr->flags & BUTTON_PRESSED) ? TK_RELIEF_SUNKEN
  812.         : TK_RELIEF_RAISED;
  813.     Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), sliderBorder,
  814.         x, y, width, 2*height, shadowWidth, relief);
  815.     x += shadowWidth;
  816.     y += shadowWidth;
  817.     width -= 2*shadowWidth;
  818.     height -= shadowWidth;
  819.     Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), sliderBorder,
  820.         x, y, width, height, shadowWidth, relief);
  821.     Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), sliderBorder,
  822.         x, y+height, width, height, shadowWidth, relief);
  823.  
  824.     /*
  825.      * Draw the label to the right of the scale.
  826.      */
  827.  
  828.     if ((scalePtr->flags & REDRAW_OTHER) && (scalePtr->labelPixels != 0)) {
  829.     XDrawString(Tk_Display(scalePtr->tkwin), Tk_WindowId(scalePtr->tkwin),
  830.         scalePtr->textGC, labelLeftEdge,
  831.         scalePtr->offset + (3*scalePtr->fontPtr->ascent)/2,
  832.         scalePtr->label, scalePtr->labelLength);
  833.     }
  834.  
  835.     /*
  836.      * Draw the window border.
  837.      */
  838.  
  839.     if ((scalePtr->flags & REDRAW_OTHER)
  840.         && (scalePtr->relief != TK_RELIEF_FLAT)) {
  841.     Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
  842.         scalePtr->bgBorder, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  843.         scalePtr->borderWidth, scalePtr->relief);
  844.     }
  845.  
  846.     done:
  847.     scalePtr->flags &= ~REDRAW_ALL;
  848. }
  849.  
  850. /*
  851.  *----------------------------------------------------------------------
  852.  *
  853.  * DisplayVerticalValue --
  854.  *
  855.  *    This procedure is called to display values (scale readings)
  856.  *    for vertically-oriented scales.
  857.  *
  858.  * Results:
  859.  *    None.
  860.  *
  861.  * Side effects:
  862.  *    The numerical value corresponding to value is displayed with
  863.  *    its right edge at "rightEdge", and at a vertical position in
  864.  *    the scale that corresponds to "value".
  865.  *
  866.  *----------------------------------------------------------------------
  867.  */
  868.  
  869. static void
  870. DisplayVerticalValue(scalePtr, value, rightEdge)
  871.     register Scale *scalePtr;    /* Information about widget in which to
  872.                  * display value. */
  873.     int value;            /* Y-coordinate of number to display,
  874.                  * specified in application coords, not
  875.                  * in pixels (we'll compute pixels). */
  876.     int rightEdge;        /* X-coordinate of right edge of text,
  877.                  * specified in pixels. */
  878. {
  879.     register Tk_Window tkwin = scalePtr->tkwin;
  880.     int y, dummy, length;
  881.     char valueString[30];
  882.     XCharStruct bbox;
  883.  
  884.     y = ValueToPixel(scalePtr, value) + scalePtr->fontPtr->ascent/2;
  885.     sprintf(valueString, "%d", value);
  886.     length = strlen(valueString);
  887.     XTextExtents(scalePtr->fontPtr, valueString, length,
  888.         &dummy, &dummy, &dummy, &bbox);
  889.  
  890.     /*
  891.      * Adjust the y-coordinate if necessary to keep the text entirely
  892.      * inside the window.
  893.      */
  894.  
  895.     if ((y - bbox.ascent) < scalePtr->offset) {
  896.     y = scalePtr->offset + bbox.ascent;
  897.     }
  898.     if ((y + bbox.descent) > (Tk_Height(tkwin) - scalePtr->offset)) {
  899.     y = Tk_Height(tkwin) - scalePtr->offset - bbox.descent;
  900.     }
  901.     XDrawString(Tk_Display(tkwin), Tk_WindowId(tkwin),
  902.         scalePtr->textGC, rightEdge - bbox.rbearing,
  903.         y, valueString, length);
  904. }
  905.  
  906. /*
  907.  *--------------------------------------------------------------
  908.  *
  909.  * DisplayHorizontalScale --
  910.  *
  911.  *    This procedure redraws the contents of a horizontal scale
  912.  *    window.  It is invoked as a do-when-idle handler, so it only
  913.  *    runs when there's nothing else for the application to do.
  914.  *
  915.  * Results:
  916.  *    None.
  917.  *
  918.  * Side effects:
  919.  *    Information appears on the screen.
  920.  *
  921.  *--------------------------------------------------------------
  922.  */
  923.  
  924. static void
  925. DisplayHorizontalScale(clientData)
  926.     ClientData clientData;    /* Information about widget. */
  927. {
  928.     register Scale *scalePtr = (Scale *) clientData;
  929.     register Tk_Window tkwin = scalePtr->tkwin;
  930.     int tickBottom, valueBottom, labelBottom, scaleBottom;
  931.     int totalPixels, x, y, width, height, shadowWidth, tickValue;
  932.     int relief;
  933.     Tk_3DBorder sliderBorder;
  934.  
  935.     if ((scalePtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  936.     goto done;
  937.     }
  938.  
  939.     /*
  940.      * Scanning from bottom to top across the window, the window
  941.      * will contain four rows:  ticks, value, scale, and label.
  942.      * Compute the y-coordinate for each of the rows.
  943.      */
  944.  
  945.     totalPixels = scalePtr->tickPixels + scalePtr->valuePixels
  946.         + 2*scalePtr->borderWidth + scalePtr->width
  947.         + scalePtr->labelPixels;
  948.     tickBottom = (Tk_Height(tkwin) + totalPixels)/2 - 1;
  949.     valueBottom = tickBottom - scalePtr->tickPixels;
  950.     scaleBottom = valueBottom - scalePtr->valuePixels;
  951.     labelBottom = scaleBottom - 2*scalePtr->borderWidth - scalePtr->width;
  952.  
  953.     /*
  954.      * Display the information from bottom to top across the window.
  955.      */
  956.  
  957.     if (scalePtr->flags & REDRAW_OTHER) {
  958.     XClearWindow(Tk_Display(tkwin), Tk_WindowId(tkwin));
  959.  
  960.     /*
  961.      * Display the tick marks.
  962.      */
  963.  
  964.     if (scalePtr->tickPixels != 0) {
  965.         for (tickValue = scalePtr->fromValue; ;
  966.             tickValue += scalePtr->tickInterval) {
  967.         if (scalePtr->toValue > scalePtr->fromValue) {
  968.             if (tickValue > scalePtr->toValue) {
  969.             break;
  970.             }
  971.         } else {
  972.             if (tickValue < scalePtr->toValue) {
  973.             break;
  974.             }
  975.         }
  976.         DisplayHorizontalValue(scalePtr, tickValue, tickBottom);
  977.         }
  978.     }
  979.     }
  980.  
  981.     /*
  982.      * Display the value, if it is desired.  If not redisplaying the
  983.      * entire window, clear the area of the value to get rid of the
  984.      * old value displayed there.
  985.      */
  986.  
  987.     if (scalePtr->showValue) {
  988.     if (!(scalePtr->flags & REDRAW_OTHER)) {
  989.         XClearArea(Tk_Display(tkwin), Tk_WindowId(tkwin),
  990.             scalePtr->offset, scaleBottom + 1,
  991.             Tk_Width(tkwin) - 2*scalePtr->offset,
  992.             valueBottom - scaleBottom, False);
  993.     }
  994.     DisplayHorizontalValue(scalePtr, scalePtr->value, valueBottom);
  995.     }
  996.  
  997.     /*
  998.      * Display the scale and the slider.  If not redisplaying the
  999.      * entire window, must clear the trench area to erase the old
  1000.      * slider, but don't need to redraw the border.
  1001.      */
  1002.  
  1003.     y = scaleBottom - 2*scalePtr->borderWidth - scalePtr->width + 1;
  1004.     if (scalePtr->flags & REDRAW_OTHER) {
  1005.     Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
  1006.         scalePtr->bgBorder, scalePtr->offset, y,
  1007.         Tk_Width(tkwin) - 2*scalePtr->offset,
  1008.         scalePtr->width + 2*scalePtr->borderWidth,
  1009.         scalePtr->borderWidth, TK_RELIEF_SUNKEN);
  1010.     } else {
  1011.     XClearArea(Tk_Display(tkwin), Tk_WindowId(tkwin),
  1012.         scalePtr->offset + scalePtr->borderWidth,
  1013.         y + scalePtr->borderWidth,
  1014.         Tk_Width(tkwin) - 2*scalePtr->offset - 2*scalePtr->borderWidth,
  1015.         scalePtr->width, False);
  1016.     }
  1017.     if (scalePtr->flags & ACTIVE) {
  1018.     sliderBorder = scalePtr->activeBorder;
  1019.     } else {
  1020.     sliderBorder = scalePtr->sliderBorder;
  1021.     }
  1022.     width = scalePtr->sliderLength/2;
  1023.     height = scalePtr->width;
  1024.     x = ValueToPixel(scalePtr, scalePtr->value) - width;
  1025.     y += scalePtr->borderWidth;
  1026.     shadowWidth = scalePtr->borderWidth/2;
  1027.     if (shadowWidth == 0) {
  1028.     shadowWidth = 1;
  1029.     }
  1030.     relief = (scalePtr->flags & BUTTON_PRESSED) ? TK_RELIEF_SUNKEN
  1031.         : TK_RELIEF_RAISED;
  1032.     Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), sliderBorder,
  1033.         x, y, 2*width, height, shadowWidth, relief);
  1034.     x += shadowWidth;
  1035.     y += shadowWidth;
  1036.     width -= shadowWidth;
  1037.     height -= 2*shadowWidth;
  1038.     Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), sliderBorder,
  1039.         x, y, width, height, shadowWidth, relief);
  1040.     Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), sliderBorder,
  1041.         x+width, y, width, height, shadowWidth, relief);
  1042.  
  1043.     /*
  1044.      * Draw the label to the top of the scale.
  1045.      */
  1046.  
  1047.     if ((scalePtr->flags & REDRAW_OTHER) && (scalePtr->labelPixels != 0)) {
  1048.     XDrawString(Tk_Display(scalePtr->tkwin), Tk_WindowId(scalePtr->tkwin),
  1049.         scalePtr->textGC, scalePtr->offset + scalePtr->fontPtr->ascent/2,
  1050.         labelBottom - scalePtr->fontPtr->descent,
  1051.         scalePtr->label, scalePtr->labelLength);
  1052.     }
  1053.  
  1054.     /*
  1055.      * Draw the window border.
  1056.      */
  1057.  
  1058.     if ((scalePtr->flags & REDRAW_OTHER)
  1059.         && (scalePtr->relief != TK_RELIEF_FLAT)) {
  1060.     Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
  1061.         scalePtr->bgBorder, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  1062.         scalePtr->borderWidth, scalePtr->relief);
  1063.     }
  1064.  
  1065.     done:
  1066.     scalePtr->flags &= ~REDRAW_ALL;
  1067. }
  1068.  
  1069. /*
  1070.  *----------------------------------------------------------------------
  1071.  *
  1072.  * DisplayHorizontalValue --
  1073.  *
  1074.  *    This procedure is called to display values (scale readings)
  1075.  *    for horizontally-oriented scales.
  1076.  *
  1077.  * Results:
  1078.  *    None.
  1079.  *
  1080.  * Side effects:
  1081.  *    The numerical value corresponding to value is displayed with
  1082.  *    its bottom edge at "bottom", and at a horizontal position in
  1083.  *    the scale that corresponds to "value".
  1084.  *
  1085.  *----------------------------------------------------------------------
  1086.  */
  1087.  
  1088. static void
  1089. DisplayHorizontalValue(scalePtr, value, bottom)
  1090.     register Scale *scalePtr;    /* Information about widget in which to
  1091.                  * display value. */
  1092.     int value;            /* Y-coordinate of number to display,
  1093.                  * specified in application coords, not
  1094.                  * in pixels (we'll compute pixels). */
  1095.     int bottom;            /* Y-coordinate of bottom edge of text,
  1096.                  * specified in pixels. */
  1097. {
  1098.     register Tk_Window tkwin = scalePtr->tkwin;
  1099.     int x, y, dummy, length;
  1100.     char valueString[30];
  1101.     XCharStruct bbox;
  1102.  
  1103.     x = ValueToPixel(scalePtr, value);
  1104.     y = bottom - scalePtr->fontPtr->descent;
  1105.     sprintf(valueString, "%d", value);
  1106.     length = strlen(valueString);
  1107.     XTextExtents(scalePtr->fontPtr, valueString, length,
  1108.         &dummy, &dummy, &dummy, &bbox);
  1109.  
  1110.     /*
  1111.      * Adjust the x-coordinate if necessary to keep the text entirely
  1112.      * inside the window.
  1113.      */
  1114.  
  1115.     x -= (bbox.lbearing + bbox.rbearing)/2;
  1116.     if ((x - bbox.lbearing) < scalePtr->offset) {
  1117.     x = scalePtr->offset + bbox.lbearing;
  1118.     }
  1119.     if ((y + bbox.rbearing) > (Tk_Width(tkwin) - scalePtr->offset)) {
  1120.     x = Tk_Width(tkwin) - scalePtr->offset - bbox.rbearing;
  1121.     }
  1122.     XDrawString(Tk_Display(tkwin), Tk_WindowId(tkwin),
  1123.         scalePtr->textGC, x, y, valueString, length);
  1124. }
  1125.  
  1126. /*
  1127.  *----------------------------------------------------------------------
  1128.  *
  1129.  * PixelToValue --
  1130.  *
  1131.  *    Given a pixel within a scale window, return the scale
  1132.  *    reading corresponding to that pixel.
  1133.  *
  1134.  * Results:
  1135.  *    An integer scale reading.
  1136.  *
  1137.  * Side effects:
  1138.  *    None.
  1139.  *
  1140.  *----------------------------------------------------------------------
  1141.  */
  1142.  
  1143. static int
  1144. PixelToValue(scalePtr, x, y)
  1145.     register Scale *scalePtr;        /* Information about widget. */
  1146.     int x, y;                /* Coordinates of point within
  1147.                      * window. */
  1148. {
  1149.     int value, pixelRange;
  1150.  
  1151.     if (scalePtr->vertical) {
  1152.     pixelRange = Tk_Height(scalePtr->tkwin) - scalePtr->sliderLength
  1153.         - 2*scalePtr->offset - 2*scalePtr->borderWidth;
  1154.     value = y;
  1155.     } else {
  1156.     pixelRange = Tk_Width(scalePtr->tkwin) - scalePtr->sliderLength
  1157.         - 2*scalePtr->offset - 2*scalePtr->borderWidth;
  1158.     value = x;
  1159.     }
  1160.  
  1161.     if (pixelRange <= 0) {
  1162.     /*
  1163.      * Not enough room for the slider to actually slide:  just return
  1164.      * the scale's current value.
  1165.      */
  1166.  
  1167.     return scalePtr->value;
  1168.     }
  1169.     value -= scalePtr->sliderLength/2 + scalePtr->offset
  1170.         + scalePtr->borderWidth;
  1171.     if (value < 0) {
  1172.     value = 0;
  1173.     }
  1174.     if (value > pixelRange) {
  1175.     value = pixelRange;
  1176.     }
  1177.     if (scalePtr->toValue > scalePtr->fromValue) {
  1178.     value = scalePtr->fromValue +
  1179.         ((value * (scalePtr->toValue - scalePtr->fromValue))
  1180.         + pixelRange/2)/pixelRange;
  1181.     } else {
  1182.     value = scalePtr->toValue +
  1183.         (((pixelRange - value)
  1184.         * (scalePtr->fromValue - scalePtr->toValue))
  1185.         + pixelRange/2)/pixelRange;
  1186.     }
  1187.     return value;
  1188. }
  1189.  
  1190. /*
  1191.  *----------------------------------------------------------------------
  1192.  *
  1193.  * ValueToPixel --
  1194.  *
  1195.  *    Given a reading of the scale, return the x-coordinate or
  1196.  *    y-coordinate corresponding to that reading, depending on
  1197.  *    whether the scale is vertical or horizontal, respectively.
  1198.  *
  1199.  * Results:
  1200.  *    An integer value giving the pixel location corresponding
  1201.  *    to reading.  The value is restricted to lie within the
  1202.  *    defined range for the scale.
  1203.  *
  1204.  * Side effects:
  1205.  *    None.
  1206.  *
  1207.  *----------------------------------------------------------------------
  1208.  */
  1209.  
  1210. static int
  1211. ValueToPixel(scalePtr, value)
  1212.     register Scale *scalePtr;        /* Information about widget. */
  1213.     int value;                /* Reading of the widget. */
  1214. {
  1215.     int y, pixelRange, valueRange;
  1216.  
  1217.     valueRange = scalePtr->toValue - scalePtr->fromValue;
  1218.     pixelRange = (scalePtr->vertical ? Tk_Height(scalePtr->tkwin)
  1219.         : Tk_Width(scalePtr->tkwin)) - scalePtr->sliderLength
  1220.         - 2*scalePtr->offset - 2*scalePtr->borderWidth;
  1221.     y = ((value - scalePtr->fromValue) * pixelRange
  1222.         + valueRange/2) / valueRange;
  1223.     if (y < 0) {
  1224.     y = 0;
  1225.     } else if (y > pixelRange) {
  1226.     y = pixelRange;
  1227.     }
  1228.     y += scalePtr->sliderLength/2 + scalePtr->offset + scalePtr->borderWidth;
  1229.     return y;
  1230. }
  1231.  
  1232. /*
  1233.  *--------------------------------------------------------------
  1234.  *
  1235.  * ScaleEventProc --
  1236.  *
  1237.  *    This procedure is invoked by the Tk dispatcher for various
  1238.  *    events on scales.
  1239.  *
  1240.  * Results:
  1241.  *    None.
  1242.  *
  1243.  * Side effects:
  1244.  *    When the window gets deleted, internal structures get
  1245.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1246.  *
  1247.  *--------------------------------------------------------------
  1248.  */
  1249.  
  1250. static void
  1251. ScaleEventProc(clientData, eventPtr)
  1252.     ClientData clientData;    /* Information about window. */
  1253.     XEvent *eventPtr;        /* Information about event. */
  1254. {
  1255.     Scale *scalePtr = (Scale *) clientData;
  1256.  
  1257.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  1258.     EventuallyRedrawScale(scalePtr, REDRAW_ALL);
  1259.     } else if (eventPtr->type == DestroyNotify) {
  1260.     Tcl_DeleteCommand(scalePtr->interp, Tk_PathName(scalePtr->tkwin));
  1261.     scalePtr->tkwin = NULL;
  1262.     if (scalePtr->flags & REDRAW_ALL) {
  1263.         if (scalePtr->vertical) {
  1264.         Tk_CancelIdleCall(DisplayVerticalScale, (ClientData) scalePtr);
  1265.         } else {
  1266.         Tk_CancelIdleCall(DisplayHorizontalScale,
  1267.             (ClientData) scalePtr);
  1268.         }
  1269.     }
  1270.     Tk_EventuallyFree((ClientData) scalePtr, DestroyScale);
  1271.     } else if (eventPtr->type == ConfigureNotify) {
  1272.     ComputeScaleGeometry(scalePtr);
  1273.     }
  1274. }
  1275.  
  1276. /*
  1277.  *--------------------------------------------------------------
  1278.  *
  1279.  * ScaleMouseProc --
  1280.  *
  1281.  *    This procedure is called back by Tk in response to
  1282.  *    mouse events such as window entry, window exit, mouse
  1283.  *    motion, and button presses.
  1284.  *
  1285.  * Results:
  1286.  *    None.
  1287.  *
  1288.  * Side effects:
  1289.  *    This procedure implements the "feel" of the scale by
  1290.  *    issuing commands in response to button presses and mouse
  1291.  *    motion.
  1292.  *
  1293.  *--------------------------------------------------------------
  1294.  */
  1295.  
  1296. static void
  1297. ScaleMouseProc(clientData, eventPtr)
  1298.     ClientData clientData;        /* Information about window. */
  1299.     register XEvent *eventPtr;        /* Information about event. */
  1300. {
  1301.     register Scale *scalePtr = (Scale *) clientData;
  1302.  
  1303.     if (scalePtr->state != tkNormalUid) {
  1304.     return;
  1305.     }
  1306.  
  1307.     Tk_Preserve((ClientData) scalePtr);
  1308.     if (eventPtr->type == EnterNotify) {
  1309.     scalePtr->flags |= ACTIVE;
  1310.     EventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
  1311.     } else if (eventPtr->type == LeaveNotify) {
  1312.     scalePtr->flags &= ~ACTIVE;
  1313.     EventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
  1314.     } else if ((eventPtr->type == MotionNotify)
  1315.         && (scalePtr->flags & BUTTON_PRESSED)) {
  1316.     SetScaleValue(scalePtr,  PixelToValue(scalePtr,
  1317.         eventPtr->xmotion.x, eventPtr->xmotion.y));
  1318.     } else if ((eventPtr->type == ButtonPress)
  1319.         && (eventPtr->xbutton.button == Button1)
  1320.         && (eventPtr->xbutton.state == 0)) {
  1321.     scalePtr->flags |= BUTTON_PRESSED;
  1322.     SetScaleValue(scalePtr, PixelToValue(scalePtr,
  1323.         eventPtr->xbutton.x, eventPtr->xbutton.y));
  1324.     EventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
  1325.     } else if ((eventPtr->type == ButtonRelease)
  1326.         && (eventPtr->xbutton.button == Button1)
  1327.         && (scalePtr->flags & BUTTON_PRESSED)) {
  1328.     scalePtr->flags &= ~BUTTON_PRESSED;
  1329.     EventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
  1330.     }
  1331.     Tk_Release((ClientData) scalePtr);
  1332. }
  1333.  
  1334. /*
  1335.  *--------------------------------------------------------------
  1336.  *
  1337.  * SetScaleValue --
  1338.  *
  1339.  *    This procedure changes the value of a scale and invokes
  1340.  *    a Tcl command to reflect the current position of a scale
  1341.  *
  1342.  * Results:
  1343.  *    None.
  1344.  *
  1345.  * Side effects:
  1346.  *    A Tcl command is invoked, and an additional error-processing
  1347.  *    command may also be invoked.  The scale's slider is redrawn.
  1348.  *
  1349.  *--------------------------------------------------------------
  1350.  */
  1351.  
  1352. static void
  1353. SetScaleValue(scalePtr, value)
  1354.     register Scale *scalePtr;    /* Info about widget. */
  1355.     int value;            /* New value for scale.  Gets
  1356.                  * adjusted if it's off the scale. */
  1357. {
  1358.     int result;
  1359.     char string[20];
  1360.  
  1361.     if ((value < scalePtr->fromValue)
  1362.         ^ (scalePtr->toValue < scalePtr->fromValue)) {
  1363.     value = scalePtr->fromValue;
  1364.     }
  1365.     if ((value > scalePtr->toValue)
  1366.         ^ (scalePtr->toValue < scalePtr->fromValue)) {
  1367.     value = scalePtr->toValue;
  1368.     }
  1369.     if (value == scalePtr->value) {
  1370.     return;
  1371.     }
  1372.     scalePtr->value = value;
  1373.     EventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
  1374.  
  1375.     sprintf(string, " %d", scalePtr->value);
  1376.     result = Tcl_VarEval(scalePtr->interp, scalePtr->command, string,
  1377.         (char *) NULL);
  1378.     if (result != TCL_OK) {
  1379.     TkBindError(scalePtr->interp);
  1380.     }
  1381. }
  1382.  
  1383. /*
  1384.  *--------------------------------------------------------------
  1385.  *
  1386.  * EventuallyRedrawScale --
  1387.  *
  1388.  *    Arrange for part or all of a scale widget to redrawn at
  1389.  *    the next convenient time in the future.
  1390.  *
  1391.  * Results:
  1392.  *    None.
  1393.  *
  1394.  * Side effects:
  1395.  *    If "what" is REDRAW_SLIDER then just the slider and the
  1396.  *    value readout will be redrawn;  if "what" is REDRAW_ALL
  1397.  *    then the entire widget will be redrawn.
  1398.  *
  1399.  *--------------------------------------------------------------
  1400.  */
  1401.  
  1402. static void
  1403. EventuallyRedrawScale(scalePtr, what)
  1404.     register Scale *scalePtr;    /* Information about widget. */
  1405.     int what;            /* What to redraw:  REDRAW_SLIDER
  1406.                  * or REDRAW_ALL. */
  1407. {
  1408.     if ((what == 0) || (scalePtr->tkwin == NULL)
  1409.         || !Tk_IsMapped(scalePtr->tkwin)) {
  1410.     return;
  1411.     }
  1412.     if ((scalePtr->flags & REDRAW_ALL) == 0) {
  1413.     if (scalePtr->vertical) {
  1414.         Tk_DoWhenIdle(DisplayVerticalScale, (ClientData) scalePtr);
  1415.     } else {
  1416.         Tk_DoWhenIdle(DisplayHorizontalScale, (ClientData) scalePtr);
  1417.     }
  1418.     }
  1419.     scalePtr->flags |= what;
  1420. }
  1421.